#pragma once
#include "SnRender.h"
#include "base/SnImage.h"
#include "base/YUVImage.h"
#include "base/shader.h"
#include <atomic>
#include <memory>
#include <mutex>
#include <queue>
#include <stdint.h>
#include <vector>


namespace Sivin {

  class VideoPboRender : public SnRender {
    static constexpr int PBO_NUM = 2;
    struct SnVertex {
      GLfloat postion[3];
      GLfloat texCoord[2];
    };

    struct YUVTexture {
      int availablePboIdx{0};
      GLuint pbo[PBO_NUM];
      GLsync sync[PBO_NUM]{};
      GLuint id;
      int width;
      int height;
      YUVImage::Channel channel{YUVImage::Channel::NONE};
    };

  public:
    explicit VideoPboRender();
    ~VideoPboRender();

  public:
    void onGLInit() override;
    void onRender() override;
    void onGLDestroy() override;

  private:
    void initVAO();

    void initTexture(YUVTexture &yuvTexture);

    void addYUVImage(std::unique_ptr<YUVImage> &imageData);

    void pollYUVImage(std::unique_ptr<YUVImage> &imageData);

    void updateGPUData();

    void sendDataToGPU(YUVTexture &yuvTexture, std::unique_ptr<YUVImage> &imageData);

    void doReadYUVLoop();

  private:
    GLuint mVAO;
    YUVTexture mYTexture;
    YUVTexture mUTexture;
    YUVTexture mVTexture;
    std::unique_ptr<Shader> mShader{nullptr};
    std::vector<SnVertex> mVertexData;
    std::vector<GLuint> mElementIndexArray;

    std::atomic_bool mRunning{true};
    std::unique_ptr<std::thread> mReadThread;
    std::queue<std::unique_ptr<YUVImage>> mYuvImageQueue;
    std::mutex mMutex;
    std::condition_variable mCond;

    int64_t lastRenderPoint{0};
  };
}// namespace Sivin